using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Serialization;

using Pegasus.Diagnostics;
using Pegasus.Runtime.Serialization;

namespace Pegasus.Runtime.Remoting.Messaging
{
	/// <summary>
	/// Internal class for passing messages as part of the remoting sinks.
	/// </summary>
	[Serializable]
	internal class PegasusMethodCall : IMethodCallMessage, IMethodReturnMessage, ISerializable
	{
		// Local Instance Values
		private IDictionary m_properties;
		
		private object[] m_inArgs = new object[ 0 ];
		private string[] m_inArgsNames = new string[ 0 ];
		private object[] m_args = new object[ 0 ];
		private string[] m_argsNames = new string[ 0 ];
		private object[] m_outArgs = new object[ 0 ];
		private string[] m_outArgsNames = new string[ 0 ];

		private bool m_hasVarArgs = false;
		private LogicalCallContext m_logicalCallContext = null;
		private string m_methodUri = null;
		private object m_returnValue = null;
		private Exception m_exception = null;

		private MethodInfo m_methodInfo = null;
		
		// Local Static Values
		private static ObjectSerializationBinder s_binder = new ObjectSerializationBinder();

		/// <summary>
		/// Initializes a new instance of the <see cref="PegasusMethodCall"/> class.
		/// </summary>
		/// <param name="message">The message.</param>
		public PegasusMethodCall( IMessage message )
		{
			// Check Parameters
			ParamCode.AssertNotNull( message, "message" );

			// Get references to all the values of the message object
			IDictionary properties = message.Properties;
			m_properties = new Hashtable();
			foreach( object key in properties.Keys )
			{
				m_properties.Add( key, properties[ key ] );
			}

			if( message is IMethodMessage )
			{
				IMethodMessage methodMessage = (IMethodMessage) message;

				m_args = methodMessage.Args;
				int count = m_args.Length;
				m_argsNames = new string[ count ];
				for( int x = 0; x < count; x++ )
				{
					m_argsNames[ x ] = methodMessage.GetArgName( x );
				}

				m_hasVarArgs = methodMessage.HasVarArgs;
				m_logicalCallContext = methodMessage.LogicalCallContext;
				m_methodUri = methodMessage.Uri;

				m_methodInfo = (MethodInfo) methodMessage.MethodBase;

				if( message is IMethodCallMessage )
				{
					IMethodCallMessage methodCall = (IMethodCallMessage) message;

					m_inArgs = methodCall.InArgs;
					count = m_inArgs.Length;
					m_inArgsNames = new string[ count ];
					for( int x = 0; x < count; x++ )
					{
						m_inArgsNames[ x ] = methodCall.GetInArgName( x );
					}
				}

				if( message is IMethodReturnMessage )
				{
					IMethodReturnMessage returnMessage = (IMethodReturnMessage) message;

					m_outArgs = returnMessage.OutArgs;
					count = m_outArgs.Length;
					m_outArgsNames = new string[ count ];
					for( int x = 0; x < count; x++ )
					{
						m_outArgsNames[ x ] = returnMessage.GetOutArgName( x );
					}

					m_returnValue = returnMessage.ReturnValue;
					m_exception = returnMessage.Exception;
				}
			}
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="PegasusMethodCall"/> class.
		/// </summary>
		/// <param name="info">The info.</param>
		/// <param name="context">The context.</param>
		public PegasusMethodCall( SerializationInfo info, StreamingContext context )
		{
			// Check Parameters
			ParamCode.AssertNotNull( info, "info" );
			ParamCode.AssertNotNull( context, "context" );

			m_properties = (IDictionary) info.GetValue( "m_properties", typeof( IDictionary ) );

			m_args = (object[]) info.GetValue( "m_args", typeof( object[] ) );
			m_argsNames = (string[]) info.GetValue( "m_argsNames", typeof( string[] ) );
			m_hasVarArgs = info.GetBoolean( "m_hasVarArgs" );
			m_inArgs = (object[]) info.GetValue( "m_inArgs", typeof( object[] ) );
			m_inArgsNames = (string[]) info.GetValue( "m_inArgsNames", typeof( string[] ) );
			m_outArgs = (object[]) info.GetValue( "m_outArgs", typeof( object[] ) );
			m_outArgsNames = (string[]) info.GetValue( "m_outArgsNames", typeof( string[] ) );

			m_returnValue = info.GetValue( "m_returnValue", typeof( object ) );
			m_exception = (Exception) info.GetValue( "m_exception", typeof( Exception ) );

			m_logicalCallContext = (LogicalCallContext) info.GetValue( "m_logicalCallContext", typeof( LogicalCallContext ) );
			m_methodUri = info.GetString( "m_methodUri" );

			string methodName = info.GetString( "MethodName" );
			string typeName = info.GetString( "TypeName" );
			string assemblyName = info.GetString( "AssemblyName" );

			// Find the method info object using the version independant binder
			Type methodType = s_binder.BindToType( assemblyName, typeName );
			if( methodType == null )
			{
				throw new SerializationException( string.Format( "Unable to find type {0} or assembly {1}", typeName, assemblyName ) );
			}

			m_methodInfo = methodType.GetMethod( methodName );
			if( m_methodInfo == null )
			{
				throw new SerializationException( string.Format( "Unable to find method {0} in type {1}", methodName, typeName ) );
			}
		}

		/// <summary>
		/// Gets an <see cref="T:System.Collections.IDictionary"></see> that represents a collection of the message's properties.
		/// </summary>
		/// <value></value>
		/// <returns>A dictionary that represents a collection of the message's properties.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public IDictionary Properties
		{
			get
			{
				return m_properties;
			}
		}

		/// <summary>
		/// Gets the number of arguments in the call that are not marked as out parameters.
		/// </summary>
		/// <value></value>
		/// <returns>The number of arguments in the call that are not marked as out parameters.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public int InArgCount
		{
			get
			{
				return m_inArgs.Length;
			}
		}

		/// <summary>
		/// Gets an array of arguments that are not marked as out parameters.
		/// </summary>
		/// <value></value>
		/// <returns>An array of arguments that are not marked as out parameters.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public object[] InArgs
		{
			get
			{
				return m_inArgs;
			}
		}

		/// <summary>
		/// Gets the number of arguments passed to the method.
		/// </summary>
		/// <value></value>
		/// <returns>The number of arguments passed to the method.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public int ArgCount
		{
			get
			{
				return m_args.Length;
			}
		}

		/// <summary>
		/// Gets an array of arguments passed to the method.
		/// </summary>
		/// <value></value>
		/// <returns>An <see cref="T:System.Object"></see> array containing the arguments passed to the method.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public object[] Args
		{
			get
			{
				return m_args;
			}
		}

		/// <summary>
		/// Gets a value indicating whether the message has variable arguments.
		/// </summary>
		/// <value></value>
		/// <returns>true if the method can accept a variable number of arguments; otherwise, false.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public bool HasVarArgs
		{
			get
			{
				return m_hasVarArgs;
			}
		}

		/// <summary>
		/// Gets the <see cref="T:System.Runtime.Remoting.Messaging.LogicalCallContext"></see> for the current method call.
		/// </summary>
		/// <value></value>
		/// <returns>Gets the <see cref="T:System.Runtime.Remoting.Messaging.LogicalCallContext"></see> for the current method call.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public LogicalCallContext LogicalCallContext
		{
			get
			{
				return m_logicalCallContext;
			}
		}

		/// <summary>
		/// Gets the <see cref="T:System.Reflection.MethodBase"></see> of the called method.
		/// </summary>
		/// <value></value>
		/// <returns>The <see cref="T:System.Reflection.MethodBase"></see> of the called method.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public MethodBase MethodBase
		{
			get
			{
				return m_methodInfo;
			}
		}

		/// <summary>
		/// Gets the name of the invoked method.
		/// </summary>
		/// <value></value>
		/// <returns>The name of the invoked method.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public string MethodName
		{
			get
			{
				return m_methodInfo.Name;
			}
		}

		/// <summary>
		/// Gets an object containing the method signature.
		/// </summary>
		/// <value></value>
		/// <returns>An object containing the method signature.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public object MethodSignature
		{
			get
			{
				ParameterInfo[] paramArray = m_methodInfo.GetParameters();
				int count = paramArray.Length;
				Type[] types = new Type[ count ];
				for( int x = 0; x < count; x++ )
				{
					types[ x ] = paramArray[ x ].ParameterType;
				}

				return types;
			}
		}

			/// <summary>
			/// Gets the full <see cref="T:System.Type"></see> name of the specific object that the call is destined for.
			/// </summary>
			/// <value></value>
			/// <returns>The full <see cref="T:System.Type"></see> name of the specific object that the call is destined for.</returns>
			/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
			/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
			public string TypeName
		{
			get
			{
				return m_methodInfo.DeclaringType.FullName;
			}
		}

		/// <summary>
		/// Gets the URI of the specific object that the call is destined for.
		/// </summary>
		/// <value></value>
		/// <returns>The URI of the remote object that contains the invoked method.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public string Uri
		{
			get
			{
				return m_methodUri;
			}
		}

		/// <summary>
		/// Gets the number of arguments in the method call marked as ref or out parameters.
		/// </summary>
		/// <value></value>
		/// <returns>The number of arguments in the method call marked as ref or out parameters.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public int OutArgCount
		{
			get
			{
				return m_outArgs.Length;
			}
		}

		/// <summary>
		/// Returns the specified argument marked as a ref or an out parameter.
		/// </summary>
		/// <value></value>
		/// <returns>The specified argument marked as a ref or an out parameter.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public object[] OutArgs
		{
			get
			{
				return m_outArgs;
			}
		}

		/// <summary>
		/// Gets the return value of the method call.
		/// </summary>
		/// <value></value>
		/// <returns>The return value of the method call.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public object ReturnValue
		{
			get
			{
				return m_returnValue;
			}
		}

		/// <summary>
		/// Gets the exception thrown during the method call.
		/// </summary>
		/// <value></value>
		/// <returns>The exception object for the method call, or null if the method did not throw an exception.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
		public Exception Exception
		{
			get
			{
				return m_exception;
			}
		}

		/// <summary>
		/// Returns the specified argument that is not marked as an out parameter.
		/// </summary>
		/// <param name="argNum">The number of the requested in argument.</param>
		/// <returns>
		/// The requested argument that is not marked as an out parameter.
		/// </returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		public object GetInArg( int argNum )
		{
			// Check Parameters
			ParamCode.AssertRange( argNum, 0, m_inArgs.Length - 1, "argNum" );

			return m_inArgs[ argNum ];
		}

		/// <summary>
		/// Returns the name of the specified argument that is not marked as an out parameter.
		/// </summary>
		/// <param name="index">The number of the requested in argument.</param>
		/// <returns>
		/// The name of a specific argument that is not marked as an out parameter.
		/// </returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		public string GetInArgName( int index )
		{
			// Check Parameters
			ParamCode.AssertRange( index, 0, m_inArgsNames.Length - 1, "index" );

			return m_inArgsNames[ index ];
		}

		/// <summary>
		/// Gets a specific argument as an <see cref="T:System.Object"></see>.
		/// </summary>
		/// <param name="argNum">The number of the requested argument.</param>
		/// <returns>The argument passed to the method.</returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		public object GetArg( int argNum )
		{
			// Check Parameters
			ParamCode.AssertRange( argNum, 0, m_args.Length - 1, "argNum" );

			return m_args[ argNum ];
		}

		/// <summary>
		/// Gets the name of the argument passed to the method.
		/// </summary>
		/// <param name="index">The number of the requested argument.</param>
		/// <returns>
		/// The name of the specified argument passed to the method, or null if the current method is not implemented.
		/// </returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		public string GetArgName( int index )
		{
			// Check Parameters
			ParamCode.AssertRange( index, 0, m_argsNames.Length - 1, "index" );

			return m_argsNames[ index ];
		}

		/// <summary>
		/// Returns the specified argument marked as a ref or an out parameter.
		/// </summary>
		/// <param name="argNum">The number of the requested argument.</param>
		/// <returns>
		/// The specified argument marked as a ref or an out parameter.
		/// </returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		public object GetOutArg( int argNum )
		{
			// Check Parameters
			ParamCode.AssertRange( argNum, 0, m_outArgs.Length - 1, "argNum" );

			return m_outArgs[ argNum ];
		}

		/// <summary>
		/// Returns the name of the specified argument marked as a ref or an out parameter.
		/// </summary>
		/// <param name="index">The number of the requested argument name.</param>
		/// <returns>
		/// The argument name, or null if the current method is not implemented.
		/// </returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller makes the call through a reference to the interface and does not have infrastructure permission. </exception>
		public string GetOutArgName( int index )
		{
			// Check Parameters
			ParamCode.AssertRange( index, 0, m_outArgsNames.Length - 1, "index" );

			return m_outArgsNames[ index ];
		}

		/// <summary>
		/// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"></see> with the data needed to serialize the target object.
		/// </summary>
		/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"></see> to populate with data.</param>
		/// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"></see>) for this serialization.</param>
		/// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
		public void GetObjectData( SerializationInfo info, StreamingContext context )
		{
			// Check Parameters
			ParamCode.AssertNotNull( info, "info" );
			ParamCode.AssertNotNull( context, "context" );

			info.AddValue( "m_properties", m_properties );

			info.AddValue( "m_args", m_args );
			info.AddValue( "m_argsNames", m_argsNames );
			info.AddValue( "m_hasVarArgs", m_hasVarArgs );
			info.AddValue( "m_inArgs", m_inArgs );
			info.AddValue( "m_inArgsNames", m_inArgsNames );
			info.AddValue( "m_outArgs", m_outArgs );
			info.AddValue( "m_outArgsNames", m_outArgsNames );

			info.AddValue( "m_returnValue", m_returnValue );
			info.AddValue( "m_exception", m_exception );

			info.AddValue( "m_logicalCallContext", m_logicalCallContext );
			info.AddValue( "m_methodUri", m_methodUri );

			// Save the method info data 
			info.AddValue( "MethodName", m_methodInfo.Name );
			info.AddValue( "TypeName", m_methodInfo.DeclaringType.FullName );
			info.AddValue( "AssemblyName", m_methodInfo.DeclaringType.Assembly.FullName );
		}
	}
}
